/*
 * pixiv_down - CLI-based downloading tool for https://www.pixiv.net.
 * Copyright (C) 2023, 2024  Mio
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
module app.util;

///
/// Attempt to make *path* safe for different operating systems.
///
/// This will replace anything that is not: a hyphen ('-'), a period ('.'),
/// an underscore ('_'), or a "word character" (includes numbers).  Any
/// characters found will be replaced by a hyphen.
///
/// Only tested on Linux and Android.
///
/// Params:
///   path = The input path to replace characters with.
///
/// Returns: A new string with the replacements made.
///
string makeSafe(string path)
{
   import std.regex : regex, replaceAll;
   import std.string : chomp, replace, strip;

   enum kReplacement = "-";

   // 2.076 compat: reg must not be 'const'. Doesn't work with replaceAll.
   auto reg = regex(`[^-\w._]`);
   const noSpaces = strip(path).replace(" ", kReplacement);
   const almostSafe = replaceAll(noSpaces, reg, kReplacement);

   return chomp(almostSafe, "._-");
}

string[] split(string self, char delimiter, int limit) {
   import std.string : indexOf;

   string[] sections;
   ptrdiff_t pointer = 0;
   ptrdiff_t lastPointer = 0;

   while ((pointer = indexOf(self, delimiter, lastPointer)) != -1) {
      if (sections.length < limit - 1) {
         sections ~= self[lastPointer..pointer];
         lastPointer = pointer + 1;
      } else {
         sections ~= self[lastPointer..$];
         pointer = self.length;
         break;
      }
   }

   if (sections.length == 0) {
      return [self];
   }

   // Final section (mainly if limit == 2)
   if (sections.length < limit) {
      sections ~= self[lastPointer..$];
   }

   return sections;
}

///
unittest
{
   import std.format;

   pragma(inline, true)
   void assertEquals(T)(T t1, T t2) {
      assert(t1 == t2, format("Values not equal.\n\tExpected: %s\n\t  Actual: %s", t1, t2));
   }

   const noMatch = "no-matches";
   const parts = noMatch.split(' ', 2);
   assertEquals(1, parts.length);
   assertEquals(noMatch, parts[0]);

   const option = "--flag=value";
   const argAndValue = option.split('=', 2);
   assertEquals(2, argAndValue.length);
   assertEquals("--flag", argAndValue[0]);
   assertEquals("value", argAndValue[1]);

   const str = "zero-one-two-three-four-five";
   const numbers = str.split('-', 3);
   assertEquals(3, numbers.length);
   assertEquals("zero", numbers[0]);
   assertEquals("one", numbers[1]);
   assertEquals("two-three-four-five", numbers[2]);
}

/**
 * Suspend the current thread for a random value between *lowest* and
 * *largest*.
 *
 * Params:
 *   lowest = The lowest value to use for the random sleep duration.
 *   largest = The largest value to use for the random sleep duration.
 *   print = Print a message to `stderr` with sleep duration.
 */
void sleep(int lowest, int largest, bool print = true)
{
    import core.thread : Thread;
    import core.time : seconds;
    import std.random : Random, uniform, unpredictableSeed;

    auto seed = Random(unpredictableSeed);
    const secs = uniform(lowest, largest + 1, seed);
    if (print) {
      import std.stdio : stderr;
      stderr.writefln("Sleeping for %d seconds...", secs);
    }
    Thread.sleep(secs.seconds);
}
